{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "# Tracing Guardrails Quickstart\n",
    "\n",
    "NeMo Guardrails supports the Open Telemetry ([OTEL](https://opentelemetry.io/)) standard, providing granular visibility into server-side latency. It automatically captures the latency of each LLM and API call, then exports this telemetry using OTEL. You can visualize this latency with any OTEL-compatible backend, including Grafana, Jaeger, Prometheus, SigNoz, New Relic, Datadog, and Honeycomb.\n",
    "\n",
    "Throughout this notebook, you'll run guardrail requests in both sequential and parallel modes and observe how parallelizing rails significantly reduces end-to-end latency when multiple input or output rails run.\n",
    "\n",
    "For more information about exporting metrics while using NeMo Guardrails, refer to [Tracing](https://docs.nvidia.com/nemo/guardrails/latest/user-guides/tracing/index.html) in the Guardrails toolkit documentation.\n",
    "\n",
    "---\n",
    "\n",
    "## Prerequisites\n",
    "\n",
    "This notebook can be run on any laptop or workstations, and doesn't require GPUS. You'll use models hosted by Nvidia. Before starting the notebook you need the following:\n",
    "\n",
    "- Python 3.10 or later.\n",
    "- An NVIDIA [build.nvidia.com](https://build.nvidia.com/) account. You'll configure Guardrails to call models hosted there to check the safety and security of LLM interactions and generate responses. You need to create an account, and then click the 'Get API Key' green button. Once you have the key, export it to the `NVIDIA_API_KEY` environment variable as below.\n",
    "\n",
    "```\n",
    "# Set the NVIDIA_API_KEY variable using your API Key \n",
    "\n",
    "export NVIDIA_API_KEY=\"nvapi-.....\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-----\n",
    "\n",
    "## Install and Import Packages\n",
    "\n",
    "Before you begin, install and import the following packages that you'll use in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install --upgrade pip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T18:37:35.030465Z",
     "start_time": "2025-08-18T18:37:35.028290Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "!pip install nemoguardrails pandas plotly langchain_nvidia_ai_endpoints aiofiles -q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T18:37:35.858952Z",
     "start_time": "2025-08-18T18:37:35.323139Z"
    }
   },
   "outputs": [],
   "source": [
    "# Import some useful modules\n",
    "import json\n",
    "import os\n",
    "from typing import Any, Dict, List\n",
    "\n",
    "import pandas as pd\n",
    "import plotly.express as px"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T18:37:36.458565Z",
     "start_time": "2025-08-18T18:37:36.456308Z"
    }
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Enter your NVIDIA API Key created on build.nvidia.com:  ········\n"
     ]
    }
   ],
   "source": [
    "# Check the NVIDIA_API_KEY environment variable is set, if not prompt for it\n",
    "import getpass\n",
    "\n",
    "api_key = os.getenv(\"NVIDIA_API_KEY\")\n",
    "\n",
    "if not api_key:\n",
    "    api_key = getpass.getpass(\"Enter your NVIDIA API Key created on build.nvidia.com: \")\n",
    "    os.environ[\"NVIDIA_API_KEY\"] = api_key"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "SEQUENTIAL_TRACE_FILE = \"sequential_trace.jsonl\"\n",
    "PARALLEL_TRACE_FILE = \"parallel_trace.jsonl\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def delete_file_if_it_exists(filename: str) -> None:\n",
    "    \"\"\"Check if a file exists, and delete it if so\"\"\"\n",
    "    if os.path.exists(filename):\n",
    "        print(f\"Deleting {filename}\")\n",
    "        os.remove(filename)\n",
    "\n",
    "\n",
    "def delete_trace_files():\n",
    "    \"\"\"Helper to delete trace files if they exist\"\"\"\n",
    "    delete_file_if_it_exists(SEQUENTIAL_TRACE_FILE)\n",
    "    delete_file_if_it_exists(PARALLEL_TRACE_FILE)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "------\n",
    "\n",
    "## Guardrail Configurations\n",
    "\n",
    "You'll use two configurations for tracing: sequential and parallel.\n",
    "\n",
    "In the sequential configuration, each input rail is processed one after the other sequentially. If a user input passes all of these input rails successfully, the inference request is then sent to the application LLM to generate a response. After that response is ready, the output rails run in sequence to check both the user's input and the LLM's response. The final response is only returned to the user if all of these checks are successful.\n",
    "\n",
    "The parallel configuration, on the other hand, runs all input and output rails simultaneously. \n",
    "\n",
    "In this notebook, you'll see how the three input rails run in parallel and significantly reduce the end-to-end latency."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Models Configuration\n",
    "\n",
    "Store the model configuration required for tracing in the dictionary format as shown below. Each model configuration entry contains `type`, `engine`, and `model` fields:\n",
    "\n",
    "* **`type`**: This field identifies the task type of a model you want to use. The keyword `main` is reserved for the application LLM, which is responsible for generating a response to the client's request. Any other model names are referenced in the Guardrail flows to build specific workflows.\n",
    "* **`engine`**: This controls the library used to communicate with the model. The `nim` engine uses [`langchain_nvidia_ai_endpoints`](https://pypi.org/project/langchain-nvidia-ai-endpoints/) to interact with NVIDIA-hosted LLMs, while the `openai` engine connects to [OpenAI-hosted models](https://platform.openai.com/docs/models).\n",
    "* **`model`**: This is the name of the specific model you want to use for the task type.    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "CONFIG_MODELS: List[Dict[str, str]] = [\n",
    "    {\n",
    "        \"type\": \"main\",\n",
    "        \"engine\": \"nim\",\n",
    "        \"model\": \"meta/llama-4-scout-17b-16e-instruct\",\n",
    "    },\n",
    "    {\n",
    "        \"type\": \"content_safety\",\n",
    "        \"engine\": \"nim\",\n",
    "        \"model\": \"nvidia/llama-3.1-nemoguard-8b-content-safety\",\n",
    "    },\n",
    "    {\n",
    "        \"type\": \"topic_control\",\n",
    "        \"engine\": \"nim\",\n",
    "        \"model\": \"nvidia/llama-3.1-nemoguard-8b-topic-control\",\n",
    "    },\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Rails\n",
    "\n",
    "The `rails` configuration section defines a workflow that executes on every client request. The high-level sections are `input` for input rails, `output` for output rails, and `config` for any additional model condfiguration. Guardrails flows reference models defined in the `CONFIG_MODELS` variable above using the `$model=<model.type>` syntax. The following list describes each section in more detail:\n",
    "\n",
    "* `input`: Input rails run on the client request only. The config below uses three classifiers to predict whether a user request is safe, on-topic, or a jailbreak attempt. These rails can be run in parallel to reduce the latency. If any of the rails predicts an unsafe input, a refusal text is returned to the user, and no LLM generation is triggered.\n",
    "* `output`: Output rails run on both client request and the LLM response to that request. The example below checks whether the LLM response to the user request is safe to return. Output rails are needed as well as input because a safe request may give an unsafe response from the LLM if it interprets the request incorrectly. A refusal text is returned to the client if the response is unsafe.\n",
    "* `config`: Any configuration used outside of a Langchain LLM interface is included in this section. The [Jailbreak detection model](https://build.nvidia.com/nvidia/nemoguard-jailbreak-detect) uses an embedding model as a feature-generation step, followed by a Random Forest classifier to detect a jailbreak attempt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def config_rails(parallel: bool) -> Dict[str, Any]:\n",
    "    \"\"\"Create the rails configuration with programmable parallel setup\"\"\"\n",
    "    return {\n",
    "        \"input\": {\n",
    "            \"parallel\": parallel,\n",
    "            \"flows\": [\n",
    "                \"content safety check input $model=content_safety\",\n",
    "                \"topic safety check input $model=topic_control\",\n",
    "                \"jailbreak detection model\",\n",
    "            ],\n",
    "        },\n",
    "        \"output\": {\"flows\": [\"content safety check output $model=content_safety\"]},\n",
    "        \"config\": {\n",
    "            \"jailbreak_detection\": {\n",
    "                \"nim_base_url\": \"https://ai.api.nvidia.com\",\n",
    "                \"nim_server_endpoint\": \"/v1/security/nvidia/nemoguard-jailbreak-detect\",\n",
    "                \"api_key_env_var\": \"NVIDIA_API_KEY\",\n",
    "            }\n",
    "        },\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tracing\n",
    "\n",
    "The tracing configuration configures the adapter and any adapter-specific controls. Here we're storing traces in JSONL format. We'll use a different filename depending on whether we have a sequential or parallel workflow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def config_tracing(filename: str) -> Dict[str, Any]:\n",
    "    \"\"\"Return a Tracing configuration with programmable filename\"\"\"\n",
    "    return {\"enabled\": True, \"adapters\": [{\"name\": \"FileSystem\", \"filepath\": filename}]}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prompts\n",
    "\n",
    "Each Nemoguard model is fine-tuned for a specific task using a customized prompt. The prompts used at inference-time have to match the fine-tuning prompt for the best model performance. We'll load these prompts from other locations in the Guardrails repo and show the content-safety and topic-control ones below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "import yaml\n",
    "\n",
    "\n",
    "def load_yaml_file(filename: str) -> Dict[str, Any]:\n",
    "    \"\"\"Load a YAML file\"\"\"\n",
    "\n",
    "    with open(filename, \"r\") as infile:\n",
    "        data = yaml.safe_load(infile)\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "content_safety_prompts = load_yaml_file(\"../../../examples/configs/content_safety/prompts.yml\")\n",
    "topic_safety_prompts = load_yaml_file(\"../../../examples/configs/topic_safety/prompts.yml\")\n",
    "all_prompts = content_safety_prompts[\"prompts\"] + topic_safety_prompts[\"prompts\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded prompt tasks:\n",
      "content_safety_check_input $model=content_safety\n",
      "content_safety_check_output $model=content_safety\n",
      "content_safety_check_input $model=llama_guard\n",
      "content_safety_check_output $model=llama_guard_2\n",
      "content_safety_check_input $model=shieldgemma\n",
      "content_safety_check_output $model=shieldgemma\n",
      "topic_safety_check_input $model=topic_control\n"
     ]
    }
   ],
   "source": [
    "all_prompt_tasks = [prompt[\"task\"] for prompt in all_prompts]\n",
    "print(\"Loaded prompt tasks:\")\n",
    "print(\"\\n\".join(all_prompt_tasks))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Content-Safety Prompt\n",
    "\n",
    "The content-safety model can be used as an input (checking the user request only) or output (checking user request and LLM response) rail. The prompt has a task definition at the top, asking the LLM to decide if the `user_input` or `bot_response` strings fall into any of the unsafe categories listed in the prompt. The final instruction is to provide a JSON object with the `User Safety` and `Response Safety` fields. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_prompt(prompts, task_name):\n",
    "    prompt_dict = [p for p in prompts[\"prompts\"] if p[\"task\"] == task_name][0]\n",
    "    prompt_content = prompt_dict[\"content\"]\n",
    "    print(prompt_content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Task: Check if there is unsafe content in the user or agent messages in the below conversation in accordance with our safety policy with the below categories. Provide a 'safe' or 'unsafe' rating for both the user message and the agent response. If there is unsafe content, please also provide a list of violated categories according to our safety policy below.\n",
      "\n",
      "<BEGIN UNSAFE CONTENT CATEGORIES>\n",
      "S1: Violence.\n",
      "S2: Sexual.\n",
      "S3: Criminal Planning/Confessions.\n",
      "S4: Guns and Illegal Weapons.\n",
      "S5: Controlled/Regulated Substances.\n",
      "S6: Suicide and Self Harm.\n",
      "S7: Sexual (minor).\n",
      "S8: Hate/Identity Hate.\n",
      "S9: PII/Privacy.\n",
      "S10: Harassment.\n",
      "S11: Threat.\n",
      "S12: Profanity.\n",
      "S13: Needs Caution.\n",
      "S14: Other.\n",
      "S15: Manipulation.\n",
      "S16: Fraud/Deception.\n",
      "S17: Malware.\n",
      "S18: High Risk Gov Decision Making.\n",
      "S19: Political/Misinformation/Conspiracy.\n",
      "S20: Copyright/Trademark/Plagiarism.\n",
      "S21: Unauthorized Advice.\n",
      "S22: Illegal Activity.\n",
      "S23: Immoral/Unethical.\n",
      "<END UNSAFE CONTENT CATEGORIES>\n",
      "\n",
      "<BEGIN CONVERSATION>\n",
      "\n",
      "user: {{ user_input }}\n",
      "\n",
      "response: agent: {{ bot_response }}\n",
      "\n",
      "<END CONVERSATION>\n",
      "\n",
      "Provide your safety assessment for the above conversation in the below output JSON format:\n",
      "{\"User Safety\": safe or unsafe rating for the user message, \"Response Safety\": safe or unsafe rating for the agent response. Omit if no agent response present. \"Safety Categories\": a comma-separated list of applicable safety categories from the provided taxonomy. Omit if all safe.}\n",
      "\n",
      "Do not include anything other than the output JSON in your response.\n",
      "Output JSON:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print_prompt(content_safety_prompts, \"content_safety_check_output $model=content_safety\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Topic-control Prompt\n",
    "\n",
    "The topic-control model is an input-only rail. It checks whether the user request relates to one of the topics that are listed in the prompt below. For this example, we're checking for anything off-topic for a customer service agent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "You are to act as a customer service agent, providing users with factual information in accordance to the knowledge base. Your role is to ensure that you respond only to relevant queries and adhere to the following guidelines\n",
      "\n",
      "Guidelines for the user messages:\n",
      "- Do not answer questions related to personal opinions or advice on user's order, future recommendations\n",
      "- Do not provide any information on non-company products or services.\n",
      "- Do not answer enquiries unrelated to the company policies.\n",
      "- Do not answer questions asking for personal details about the agent or its creators.\n",
      "- Do not answer questions about sensitive topics related to politics, religion, or other sensitive subjects.\n",
      "- If a user asks topics irrelevant to the company's customer service relations, politely redirect the conversation or end the interaction.\n",
      "- Your responses should be professional, accurate, and compliant with customer relations guidelines, focusing solely on providing transparent, up-to-date information about the company that is already publicly available.\n",
      "- allow user comments that are related to small talk and chit-chat.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print_prompt(topic_safety_prompts, \"topic_safety_check_input $model=topic_control\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Putting All Configurations Together\n",
    "\n",
    "Use the helper functions, model definitions, and prompts from the above cells and create the sequential and parallel configurations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "SEQUENTIAL_CONFIG = {\n",
    "    \"models\": CONFIG_MODELS,\n",
    "    \"rails\": config_rails(parallel=False),\n",
    "    \"tracing\": config_tracing(filename=SEQUENTIAL_TRACE_FILE),\n",
    "    \"prompts\": all_prompts,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "PARALLEL_CONFIG = {\n",
    "    \"models\": CONFIG_MODELS,\n",
    "    \"rails\": config_rails(parallel=True),\n",
    "    \"tracing\": config_tracing(filename=PARALLEL_TRACE_FILE),\n",
    "    \"prompts\": all_prompts,\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-------\n",
    "\n",
    "# Tracing Guardrails Requests\n",
    "\n",
    "In this section of the notebook, you'll create Guardrails using the sequential config file from above. After running inference with Guardrails, you'll examine the traces and relate this to the sequence-of-events when clients make a request to Guardrails."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Running Sequential Request\n",
    "\n",
    "To run a sequential request, you'll create a `RailsConfig` object with the sequential config YAML files from above. After you have that, you can create an LLMRails object and use it to issue guardrail inference requests.\n",
    "\n",
    "You'll send a safe request, followed by an unsafe request. Guardrails will allow the safe request through to the Application LLM (and return the response), and block the unsafe request before sending to the Application LLM."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T18:37:40.231716Z",
     "start_time": "2025-08-18T18:37:40.228434Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "import nest_asyncio\n",
    "\n",
    "# Need to run this command when running in a notebook\n",
    "nest_asyncio.apply()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-08-18T18:37:41.172531Z",
     "start_time": "2025-08-18T18:37:40.773719Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Deleting sequential_trace.jsonl\n"
     ]
    }
   ],
   "source": [
    "from nemoguardrails import LLMRails, RailsConfig\n",
    "\n",
    "sequential_rails_config = RailsConfig.model_validate(SEQUENTIAL_CONFIG)\n",
    "sequential_rails = LLMRails(sequential_rails_config)\n",
    "\n",
    "safe_request = \"Respond only with a short paragraph: what is the company policy on PTO? \"\n",
    "unsafe_request = \"What's the easiest way to set a house on fire?\"\n",
    "\n",
    "# By default, we'll append to the JSONL files. Want to delete to recreate each time\n",
    "delete_file_if_it_exists(SEQUENTIAL_TRACE_FILE)\n",
    "\n",
    "safe_response = await sequential_rails.generate_async(messages=[{\"role\": \"user\", \"content\": safe_request}])\n",
    "unsafe_response = await sequential_rails.generate_async(messages=[{\"role\": \"user\", \"content\": unsafe_request}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Safe response: Our company policy on Paid Time Off, or PTO, is quite comprehensive and designed to support a healthy work-life balance. Full-time employees are eligible to accrue PTO from their hire date, with the accrual rate increasing with tenure. For example, employees with up to two years of service accrue 15 days of PTO per year, while those with five or more years accrue 25 days. Part-time employees accrue PTO on a pro-rata basis. Additionally, we offer a flexible PTO policy that allows employees to use their accrued time off for vacation, sick leave, or personal days, with the understanding that they must ensure their work responsibilities are covered during their absence. It's also worth noting that we have a blackout period around the holidays where PTO requests are not accepted, but this is communicated well in advance. If you have any specific questions or need more details, I'd be happy to help!\n"
     ]
    }
   ],
   "source": [
    "print(f\"Safe response: {safe_response.response[0]['content']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unsafe response: I'm sorry, I can't respond to that.\n"
     ]
    }
   ],
   "source": [
    "print(f\"Unsafe response: {unsafe_response.response[0]['content']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Running Parallel requests\n",
    "\n",
    "You'll now send the same safe and unsafe requests, this time using the parallel rails configuration to check their safety and security. The responses from Guardrails should match the sequential case above, since they don't depend on how we orchestrate the rail-calling."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Deleting parallel_trace.jsonl\n"
     ]
    }
   ],
   "source": [
    "from nemoguardrails import LLMRails, RailsConfig\n",
    "\n",
    "parallel_rails_config = RailsConfig.model_validate(PARALLEL_CONFIG)\n",
    "parallel_rails = LLMRails(parallel_rails_config)\n",
    "\n",
    "# By default, we'll append to the JSONL files. Want to delete to recreate each time\n",
    "delete_file_if_it_exists(PARALLEL_TRACE_FILE)\n",
    "\n",
    "safe_response = await parallel_rails.generate_async(messages=[{\"role\": \"user\", \"content\": safe_request}])\n",
    "unsafe_response = await parallel_rails.generate_async(messages=[{\"role\": \"user\", \"content\": unsafe_request}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Safe response: Our company policy on Paid Time Off, or PTO, is quite comprehensive. Full-time employees are eligible to accrue up to 15 days of PTO per year, which can be used for vacation, sick leave, or personal days. The accrual rate increases with tenure, so after three years of service, employees can accrue up to 20 days per year, and after five years, it's up to 25 days per year. PTO can be taken as soon as it's accrued, but we do have a blackout period around the holidays and during our annual company shutdown, which usually occurs in late December and early January. Employees are also allowed to carry over up to five days of unused PTO into the next year, but we encourage taking time off to recharge and relax!\n"
     ]
    }
   ],
   "source": [
    "print(f\"Safe response: {safe_response.response[0]['content']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unsafe response: I'm sorry, I can't respond to that.\n"
     ]
    }
   ],
   "source": [
    "print(f\"Unsafe response: {unsafe_response.response[0]['content']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now you've run both sequential and parallel Guardrails on an identical request, the trace JSONL files will be created with metrics of latency through the system. Now you can move on and analyze these below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-------\n",
    "\n",
    "## Analyze, Visualize, and Compare Guardrails Traces\n",
    "\n",
    "### Create Helper Functions\n",
    "\n",
    "The following cells create helper functions to load the sequential and parallel trace files into a Pandas DataFrame for analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_trace_file(filename):\n",
    "    \"\"\"Load the JSONL format, converting into a list of dicts\"\"\"\n",
    "    data = []\n",
    "    try:\n",
    "        with open(filename) as infile:\n",
    "            for line in infile:\n",
    "                data.append(json.loads(line))\n",
    "        print(f\"Loaded {len(data)} lines from {filename}\")\n",
    "    except FileNotFoundError as e:\n",
    "        print(f\"Couldn't load file {filename}, please rerun the notebook from the start\")\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_trace_data(trace_json_filename):\n",
    "    \"\"\"Load a trace JSON file, returning pandas Dataframe\"\"\"\n",
    "\n",
    "    trace_data = load_trace_file(trace_json_filename)\n",
    "\n",
    "    # Use the file creation time as a start time for the traces and spans\n",
    "    file_epoch_seconds = int(os.path.getctime(trace_json_filename))\n",
    "\n",
    "    all_trace_dfs = []\n",
    "    for trace in trace_data:\n",
    "        trace_id = trace[\"trace_id\"]\n",
    "        trace_spans = trace[\"spans\"]\n",
    "\n",
    "        trace_df = pd.DataFrame(trace_spans)\n",
    "        trace_df[\"trace_id\"] = trace_id\n",
    "        trace_df[\"epoch_seconds\"] = file_epoch_seconds\n",
    "        all_trace_dfs.append(trace_df)\n",
    "\n",
    "    all_trace_df = pd.concat(all_trace_dfs, axis=0)\n",
    "    return all_trace_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_trace_dataframe(input_df):\n",
    "    \"\"\"Clean the trace dataframe by removing all but the top-level interaction and spans\"\"\"\n",
    "\n",
    "    df = input_df.copy()\n",
    "\n",
    "    # Add boolean indicators for rails and the top-level span. We only want to keep these\n",
    "    df[\"is_rail\"] = df[\"name\"] == \"guardrails.rail\"\n",
    "    df[\"is_top_span\"] = df[\"parent_id\"].isna()\n",
    "    row_mask = df[\"is_rail\"] | df[\"is_top_span\"]\n",
    "    df = df[row_mask].copy()\n",
    "\n",
    "    # Extract each rail name from the attributes dict. Top-level span doesn't have one\n",
    "    df[\"rail_name\"] = df[\"attributes\"].apply(lambda x: x.get(\"rail.name\", None))\n",
    "    df[\"rail_name_short\"] = df[\"rail_name\"].apply(lambda x: \" \".join(x.split()[:4]) if x else x)\n",
    "\n",
    "    # Plotly Gantt charts require a proper datatime rather than relative seconds\n",
    "    # So use the creation-time of each trace file as the absolute start-point of the trace\n",
    "    df[\"start_dt\"] = pd.to_datetime(df[\"start_time\"] + df[\"epoch_seconds\"], unit=\"s\")\n",
    "    df[\"end_dt\"] = pd.to_datetime(df[\"end_time\"] + df[\"epoch_seconds\"], unit=\"s\")\n",
    "\n",
    "    # Add a boolean to the safe request trace (the first in the trace data)\n",
    "    trace_ids = df[\"trace_id\"].unique()\n",
    "    trace_id_to_num_lookup = {trace_id: idx for idx, trace_id in enumerate(trace_ids)}\n",
    "    df[\"trace_num\"] = df[\"trace_id\"].apply(lambda x: trace_id_to_num_lookup[x])\n",
    "    df[\"is_safe\"] = df[\"trace_id\"] == trace_ids[0]\n",
    "    df.index = range(df.shape[0])\n",
    "    print(f\"Found {len(trace_ids)} traces\")\n",
    "\n",
    "    # Print out some summary stats on how many spans and rails were found\n",
    "    n_top_spans = df[\"is_top_span\"].sum()\n",
    "    n_rail_spans = df[\"is_rail\"].sum()\n",
    "    print(f\"Found {n_top_spans} top-level spans, {n_rail_spans} rail spans\")\n",
    "    return df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Loading Trace Files\n",
    "\n",
    "Using the helper functions, load and clean up the sequential and parallel data. You'll see two traces, labelled with trace_num. The safe request produced the trace_num 0 trace, with the unsafe request producing trace 1. \n",
    "\n",
    "The safe request passes through all input rails (content safety, topic safety, and jailbreak detection) before being passed to the Application LLM (generate user intent). The LLM response is then checked by the content safety check output rail before being returned to the user.\n",
    "\n",
    "The unsafe request is blocked by the content safety and/or topic-control. In this case, the request is not forwarded to the Application LLM, so no 'generate user intent' or output rails are run.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded 2 lines from sequential_trace.jsonl\n",
      "Found 2 traces\n",
      "Found 2 top-level spans, 6 rail spans\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>trace_num</th>\n",
       "      <th>rail_name_short</th>\n",
       "      <th>name</th>\n",
       "      <th>is_safe</th>\n",
       "      <th>duration</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>None</td>\n",
       "      <td>guardrails.request</td>\n",
       "      <td>True</td>\n",
       "      <td>3.810076</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>content safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.403598</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>topic safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.324701</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>jailbreak detection model</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.300511</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>generate user intent</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>2.236309</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0</td>\n",
       "      <td>content safety check output</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.532284</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>None</td>\n",
       "      <td>guardrails.request</td>\n",
       "      <td>False</td>\n",
       "      <td>0.610056</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>content safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>False</td>\n",
       "      <td>0.610056</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   trace_num              rail_name_short                name  is_safe  \\\n",
       "0          0                         None  guardrails.request     True   \n",
       "1          0   content safety check input     guardrails.rail     True   \n",
       "2          0     topic safety check input     guardrails.rail     True   \n",
       "3          0    jailbreak detection model     guardrails.rail     True   \n",
       "4          0         generate user intent     guardrails.rail     True   \n",
       "5          0  content safety check output     guardrails.rail     True   \n",
       "6          1                         None  guardrails.request    False   \n",
       "7          1   content safety check input     guardrails.rail    False   \n",
       "\n",
       "   duration  \n",
       "0  3.810076  \n",
       "1  0.403598  \n",
       "2  0.324701  \n",
       "3  0.300511  \n",
       "4  2.236309  \n",
       "5  0.532284  \n",
       "6  0.610056  \n",
       "7  0.610056  "
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "raw_sequential_df = load_trace_data(SEQUENTIAL_TRACE_FILE)\n",
    "sequential_df = clean_trace_dataframe(raw_sequential_df)\n",
    "sequential_df[[\"trace_num\", \"rail_name_short\", \"name\", \"is_safe\", \"duration\"]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded 2 lines from parallel_trace.jsonl\n",
      "Found 2 traces\n",
      "Found 2 top-level spans, 7 rail spans\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>trace_num</th>\n",
       "      <th>rail_name_short</th>\n",
       "      <th>name</th>\n",
       "      <th>is_safe</th>\n",
       "      <th>duration</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>None</td>\n",
       "      <td>guardrails.request</td>\n",
       "      <td>True</td>\n",
       "      <td>2.917370</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>content safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.421178</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>topic safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.338333</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>jailbreak detection model</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.284210</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>generate user intent</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>1.977735</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0</td>\n",
       "      <td>content safety check output</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>True</td>\n",
       "      <td>0.514885</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1</td>\n",
       "      <td>None</td>\n",
       "      <td>guardrails.request</td>\n",
       "      <td>False</td>\n",
       "      <td>0.329526</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1</td>\n",
       "      <td>jailbreak detection model</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>False</td>\n",
       "      <td>0.302264</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1</td>\n",
       "      <td>topic safety check input</td>\n",
       "      <td>guardrails.rail</td>\n",
       "      <td>False</td>\n",
       "      <td>0.000013</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   trace_num              rail_name_short                name  is_safe  \\\n",
       "0          0                         None  guardrails.request     True   \n",
       "1          0   content safety check input     guardrails.rail     True   \n",
       "2          0     topic safety check input     guardrails.rail     True   \n",
       "3          0    jailbreak detection model     guardrails.rail     True   \n",
       "4          0         generate user intent     guardrails.rail     True   \n",
       "5          0  content safety check output     guardrails.rail     True   \n",
       "6          1                         None  guardrails.request    False   \n",
       "7          1    jailbreak detection model     guardrails.rail    False   \n",
       "8          1     topic safety check input     guardrails.rail    False   \n",
       "\n",
       "   duration  \n",
       "0  2.917370  \n",
       "1  0.421178  \n",
       "2  0.338333  \n",
       "3  0.284210  \n",
       "4  1.977735  \n",
       "5  0.514885  \n",
       "6  0.329526  \n",
       "7  0.302264  \n",
       "8  0.000013  "
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "raw_parallel_df = load_trace_data(PARALLEL_TRACE_FILE)\n",
    "parallel_df = clean_trace_dataframe(raw_parallel_df)\n",
    "parallel_df[[\"trace_num\", \"rail_name_short\", \"name\", \"is_safe\", \"duration\"]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Analyze Trace Data\n",
    "\n",
    "The DataFrame below shows the time (in seconds) for the top-level end-to-end interaction, and each of the rails that are called during the interaction. These all run sequentially in this configuration. All input rails have to pass before the user query is passed to the LLM. \n",
    "\n",
    "In the DataFrame below, the top-level span is labelled with the  `is_top_span` boolean, and represents the end-to-end server-side duration of the `generate_async()` call. Each top-level span for a safe request comprises 5 rail actions, which are:\n",
    "\n",
    " * `content safety check input` : Time to check the user input by the [Content-safety Nemoguard NIM](https://build.nvidia.com/nvidia/llama-3_1-nemoguard-8b-content-safety).\n",
    " * `topic safety check input` : Time to check user input by the [Topic-Control Nemoguard NIM](https://build.nvidia.com/nvidia/llama-3_1-nemoguard-8b-topic-control).\n",
    " * `jailbreak detection model` : Time to check the user input by the [Jailbreak Nemoguard NIM](https://build.nvidia.com/nvidia/nemoguard-jailbreak-detect).\n",
    " * `generate user intent` : Time to generate a response to the user's question from the Main LLM ([Llama 3.1 8B Instruct](https://build.nvidia.com/meta/llama-3_1-8b-instruct)).\n",
    " * `content safety check output` : Time to check the user input and LLM response by the [Content-safety Nemoguard NIM](https://build.nvidia.com/nvidia/llama-3_1-nemoguard-8b-content-safety).\n",
    "\n",
    "The durations should be roughly in the 400ms - 600ms range, depending on user traffic. The Llama 3.1 8B Instruct model that generates the response is an order of magnitude larger than the NemoGuard models, so it may take up to a minute to generate a response, depending on the cluster load."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Analyze Sequential Trace Data\n",
    "\n",
    "Plot the individual rail times, and a Gantt chart showing start and end-times of each rail."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "PLOT_WIDTH = 800\n",
    "PLOT_HEIGHT = 400"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>is_rail</th>\n",
       "      <th>is_top_span</th>\n",
       "      <th>rail_name_short</th>\n",
       "      <th>duration</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>None</td>\n",
       "      <td>3.810076</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>content safety check input</td>\n",
       "      <td>0.403598</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>topic safety check input</td>\n",
       "      <td>0.324701</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>jailbreak detection model</td>\n",
       "      <td>0.300511</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>generate user intent</td>\n",
       "      <td>2.236309</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>content safety check output</td>\n",
       "      <td>0.532284</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>None</td>\n",
       "      <td>0.610056</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>True</td>\n",
       "      <td>False</td>\n",
       "      <td>content safety check input</td>\n",
       "      <td>0.610056</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   is_rail  is_top_span              rail_name_short  duration\n",
       "0    False         True                         None  3.810076\n",
       "1     True        False   content safety check input  0.403598\n",
       "2     True        False     topic safety check input  0.324701\n",
       "3     True        False    jailbreak detection model  0.300511\n",
       "4     True        False         generate user intent  2.236309\n",
       "5     True        False  content safety check output  0.532284\n",
       "6    False         True                         None  0.610056\n",
       "7     True        False   content safety check input  0.610056"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sequential_df[[\"is_rail\", \"is_top_span\", \"rail_name_short\", \"duration\"]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "hovertemplate": "Rail Name=%{x}<br>Duration (seconds)=%{y}<extra></extra>",
         "legendgroup": "",
         "marker": {
          "color": "#636efa",
          "pattern": {
           "shape": ""
          }
         },
         "name": "",
         "orientation": "v",
         "showlegend": false,
         "textposition": "auto",
         "type": "bar",
         "x": [
          "generate user intent",
          "content safety check output",
          "content safety check input",
          "topic safety check input",
          "jailbreak detection model"
         ],
         "xaxis": "x",
         "y": {
          "bdata": "AAAAAPbjAUAAAACAeAjhPwAAAACM1Nk/AAAAAObH1D8AAAAAkjvTPw==",
          "dtype": "f8"
         },
         "yaxis": "y"
        }
       ],
       "layout": {
        "barmode": "relative",
        "height": 800,
        "legend": {
         "tracegroupgap": 0
        },
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Sequential Guardrails Rail durations (safe request)"
        },
        "width": 800,
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Rail Name"
         }
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Duration (seconds)"
         }
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Now let's plot a bar-graph of these numbers\n",
    "px.bar(\n",
    "    sequential_df[sequential_df[\"is_safe\"] & sequential_df[\"is_rail\"]].sort_values(\"duration\", ascending=False),\n",
    "    x=\"rail_name_short\",\n",
    "    y=\"duration\",\n",
    "    title=\"Sequential Guardrails Rail durations (safe request)\",\n",
    "    labels={\"rail_name_short\": \"Rail Name\", \"duration\": \"Duration (seconds)\"},\n",
    "    width=PLOT_WIDTH,\n",
    "    height=PLOT_HEIGHT * 2,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Gantt chart below illustrates the sequence of rails in the sequential configuration. All input rails run sequentially as expected. Once these three input rails validate that the user's request is safe, it's sent to the main application LLM. After the main LLM generates a response, the content-safety output rail checks it before it is returned to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "base": [
          "2025-09-05T14:42:28.000000000",
          "2025-09-05T14:42:28.404770136",
          "2025-09-05T14:42:28.731706858",
          "2025-09-05T14:42:29.041482925",
          "2025-09-05T14:42:31.277791977"
         ],
         "hovertemplate": "start_dt=%{base}<br>end_dt=%{x}<br>Rail Name=%{y}<extra></extra>",
         "legendgroup": "",
         "marker": {
          "color": "#636efa",
          "pattern": {
           "shape": ""
          }
         },
         "name": "",
         "orientation": "h",
         "showlegend": false,
         "textposition": "auto",
         "type": "bar",
         "x": {
          "bdata": "kwFEASwBvAgUAg==",
          "dtype": "i2"
         },
         "xaxis": "x",
         "y": [
          "content safety check input",
          "topic safety check input",
          "jailbreak detection model",
          "generate user intent",
          "content safety check output"
         ],
         "yaxis": "y"
        }
       ],
       "layout": {
        "barmode": "overlay",
        "height": 400,
        "legend": {
         "tracegroupgap": 0
        },
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Gantt chart of rails calls in sequential mode (safe request)"
        },
        "width": 800,
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          1
         ],
         "type": "date"
        },
        "yaxis": {
         "anchor": "x",
         "autorange": "reversed",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Rail Name"
         }
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Let's plot a Gantt chart, to show the sequence of when the rails execute\n",
    "\n",
    "fig = px.timeline(\n",
    "    sequential_df.loc[sequential_df[\"is_safe\"] & sequential_df[\"is_rail\"]],\n",
    "    x_start=\"start_dt\",\n",
    "    x_end=\"end_dt\",\n",
    "    y=\"rail_name_short\",\n",
    "    title=\"Gantt chart of rails calls in sequential mode (safe request)\",\n",
    "    labels={\"rail_name_short\": \"Rail Name\"},\n",
    "    width=PLOT_WIDTH,\n",
    "    height=PLOT_HEIGHT,\n",
    ")\n",
    "fig.update_yaxes(autorange=\"reversed\")\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Analyze Parallel Trace Data\n",
    "\n",
    "Plot the individual rail times from the parallel rail trace file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "hovertemplate": "Rail Name=%{x}<br>Duration (seconds)=%{y}<extra></extra>",
         "legendgroup": "",
         "marker": {
          "color": "#636efa",
          "pattern": {
           "shape": ""
          }
         },
         "name": "",
         "orientation": "v",
         "showlegend": false,
         "textposition": "auto",
         "type": "bar",
         "x": [
          "generate user intent",
          "content safety check output",
          "content safety check input",
          "topic safety check input",
          "jailbreak detection model"
         ],
         "xaxis": "x",
         "y": {
          "bdata": "AAAAwM2k/z8AAACA73ngPwAAAACU9No/AAAAAEGn1T8AAAAAgDDSPw==",
          "dtype": "f8"
         },
         "yaxis": "y"
        }
       ],
       "layout": {
        "barmode": "relative",
        "height": 800,
        "legend": {
         "tracegroupgap": 0
        },
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Parallel Guardrails Rail durations (safe request)"
        },
        "width": 800,
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Rail Name"
         }
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Duration (seconds)"
         }
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Now let's plot a bar-graph of these numbers\n",
    "px.bar(\n",
    "    parallel_df[parallel_df[\"is_safe\"] & parallel_df[\"is_rail\"]].sort_values(\"duration\", ascending=False),\n",
    "    x=\"rail_name_short\",\n",
    "    y=\"duration\",\n",
    "    title=\"Parallel Guardrails Rail durations (safe request)\",\n",
    "    labels={\"rail_name_short\": \"Rail Name\", \"duration\": \"Duration (seconds)\"},\n",
    "    width=PLOT_WIDTH,\n",
    "    height=PLOT_HEIGHT * 2,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Gantt chart below illustrates the sequence of rails in the parallel configuration. All input rails run simultaneously as expected. Once these three input rails validate that the user's request is safe, it's sent to the main application LLM. After the main LLM generates a response, the content-safety output rail checks it before it is returned to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "base": [
          "2025-09-05T14:42:31.000000000",
          "2025-09-05T14:42:31.000024796",
          "2025-09-05T14:42:31.000034809",
          "2025-09-05T14:42:31.424749851",
          "2025-09-05T14:42:33.402485132"
         ],
         "hovertemplate": "start_dt=%{base}<br>end_dt=%{x}<br>Rail Name=%{y}<extra></extra>",
         "legendgroup": "",
         "marker": {
          "color": "#636efa",
          "pattern": {
           "shape": ""
          }
         },
         "name": "",
         "orientation": "h",
         "showlegend": false,
         "textposition": "auto",
         "type": "bar",
         "x": {
          "bdata": "pQFSARwBuQcCAg==",
          "dtype": "i2"
         },
         "xaxis": "x",
         "y": [
          "content safety check input",
          "topic safety check input",
          "jailbreak detection model",
          "generate user intent",
          "content safety check output"
         ],
         "yaxis": "y"
        }
       ],
       "layout": {
        "barmode": "overlay",
        "height": 400,
        "legend": {
         "tracegroupgap": 0
        },
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Gantt chart of rails calls in parallel mode (safe request)"
        },
        "width": 800,
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          1
         ],
         "type": "date"
        },
        "yaxis": {
         "anchor": "x",
         "autorange": "reversed",
         "domain": [
          0,
          1
         ],
         "title": {
          "text": "Rail Name"
         }
        }
       }
      },
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Let's plot a Gantt chart, to show the sequence of when the rails execute\n",
    "\n",
    "fig = px.timeline(\n",
    "    parallel_df.loc[parallel_df[\"is_safe\"] & parallel_df[\"is_rail\"]],\n",
    "    x_start=\"start_dt\",\n",
    "    x_end=\"end_dt\",\n",
    "    y=\"rail_name_short\",\n",
    "    title=\"Gantt chart of rails calls in parallel mode (safe request)\",\n",
    "    labels={\"rail_name_short\": \"Rail Name\"},\n",
    "    width=PLOT_WIDTH,\n",
    "    height=PLOT_HEIGHT,\n",
    ")\n",
    "fig.update_yaxes(autorange=\"reversed\")\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Compare Sequential and Parallel Trace Data\n",
    "\n",
    "The following cells compare the input rail times for the sequential and parallel configurations. The latency difference between sequential and parallel rails is shown in the plots above. In sequential mode, the input-rail checking time is the sum of all three models. In parallel mode, the input-rail checking time is the maximum of the three rails. Let's quantify the time-saving below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "INPUT_RAIL_NAMES = {\n",
    "    \"content safety check input\",\n",
    "    \"topic safety check input\",\n",
    "    \"jailbreak detection model\",\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sequential input rail time: 1.0288s\n"
     ]
    }
   ],
   "source": [
    "# Final summary of the time-saving due to parallel rails\n",
    "\n",
    "# Sum the sequential rail run-times\n",
    "sequential_input_rail_time = sequential_df.loc[\n",
    "    sequential_df[\"is_safe\"]  # Use the safe user-request\n",
    "    & sequential_df[\"rail_name_short\"].isin(INPUT_RAIL_NAMES),\n",
    "    \"duration\",  # Use input-rails only\n",
    "].sum()\n",
    "print(f\"Sequential input rail time: {sequential_input_rail_time:.4f}s\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Parallel input rail time: 0.4212s\n",
      "Parallel input speedup: 2.4427 times\n"
     ]
    }
   ],
   "source": [
    "# Final summary of the time-saving due to parallel rails\n",
    "parallel_input_rail_time = parallel_df.loc[\n",
    "    parallel_df[\"is_safe\"] & parallel_df[\"rail_name_short\"].isin(INPUT_RAIL_NAMES),\n",
    "    \"duration\",\n",
    "].max()\n",
    "print(f\"Parallel input rail time: {parallel_input_rail_time:.4f}s\")\n",
    "print(f\"Parallel input speedup: {sequential_input_rail_time / parallel_input_rail_time:.4f} times\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check the difference in overall time\n",
    "total_sequential_time_s = sequential_df.loc[sequential_df[\"is_safe\"] & sequential_df[\"is_rail\"], \"duration\"].sum()\n",
    "total_parallel_time_s = parallel_df.loc[parallel_df[\"is_safe\"] & parallel_df[\"is_rail\"], \"duration\"].sum()\n",
    "\n",
    "parallel_time_saved_s = total_sequential_time_s - total_parallel_time_s\n",
    "parallel_time_saved_pct = (100.0 * parallel_time_saved_s) / total_sequential_time_s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total sequential time: 3.80s\n",
      "Total parallel time: 3.54s\n",
      "Time saving: 0.26s, (6.87%)\n"
     ]
    }
   ],
   "source": [
    "print(f\"Total sequential time: {total_sequential_time_s:.2f}s\")\n",
    "print(f\"Total parallel time: {total_parallel_time_s:.2f}s\")\n",
    "print(f\"Time saving: {parallel_time_saved_s:.2f}s, ({parallel_time_saved_pct:.2f}%)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-----\n",
    "\n",
    "# Conclusions\n",
    "\n",
    "In this notebook, you learned how to trace Guardrails requests in both **sequential** and **parallel** modes. By sending a single request for each mode, you were able to trace and compare their latencies. Using the graphing tools, you visualized the latency breakdown into a table, bar chart, and Gantt chart, providing a clear visual comparison of how each mode performed. The Gantt charts for parallel and sequential rails clearly show the benefit of running all three in parallel, rather than sequentially. \n",
    "\n",
    "For the sample configuration and input request run in this notebook snapshot, running the input rails in parallel mode was ~2.44x faster, reducing overall latency by 6.86% for this example. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
